package ru.spb.xmlreshelper.util;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class EventsSupport<V extends AppEvent, T extends AppEventListener<V>> {

	Map<Object, List<T>> ownersToListeners = new ConcurrentHashMap<Object, List<T>>();

	private List<T> listeners = new CopyOnWriteArrayList<T>();

	public synchronized boolean addEventListener(Object owner, T listener) {
		if (listeners.indexOf(listener) < 0) {
			put(owner, listener);
			listeners.add(0,listener);
			return true;
		} else {
			return false;
		}
	}

	private void put(Object owner, T listener) {
		List<T> objectListeners = ownersToListeners.get(owner);
		if (objectListeners == null) {
			objectListeners = new LinkedList<T>();
			ownersToListeners.put(owner, objectListeners);
		}
		if (!objectListeners.contains(listener))
			objectListeners.add(listener);
	}

	public boolean removeEventListener(Object owner, T listener) {
		return removeListener(owner, listener);
	}
	
	public boolean removeAllListeners(Object owner) {
		List<T> listenersToRemove = ownersToListeners.get(owner);
		if (listenersToRemove != null) {
			for (T listener : listenersToRemove)
				listeners.remove(listener);
		}
		return ownersToListeners.remove(owner) != null;
	}

	private boolean removeListener(Object owner, T listener) {
		List<T> listeners = ownersToListeners.get(owner);
		if (listeners != null) {
			listeners.remove(listener);
			return listeners.remove(listener);
		}
		else
			return false;
	}

	private boolean contains(Object owner, T listener) {
		List<T> objectListeners = ownersToListeners.get(owner);
		if (objectListeners == null)
			return false;
		else
			return objectListeners.contains(listener);
	}
	
	public List<T> getListenersSnapshot() {
		return new LinkedList<T>(listeners);
	}

	public void fireAppEvent(V event) {
		for (T listener : listeners) {
			if (event.isConsumed())
				break;
			else {
				try {
					listener.action(event);
				} catch (Exception e) {

					if (stopOnError())
						break;
				}
			}
		}
	}

	protected boolean stopOnError() {
		return false;
	}

}
